bitkeeper revision 1.1159.258.141 (428de6e3qI6WPj3ZDv-N9guEb9d7uA)
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 20 May 2005 13:32:19 +0000 (13:32 +0000)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Fri, 20 May 2005 13:32:19 +0000 (13:32 +0000)
Blkfront cleanups and fix whole-device mapping.
Signed-off-by: Keir Fraser <keir@xensource.com>
linux-2.6.11-xen-sparse/drivers/xen/blkfront/blkfront.c
linux-2.6.11-xen-sparse/drivers/xen/blkfront/block.h
linux-2.6.11-xen-sparse/drivers/xen/blkfront/vbd.c

index c6035b7b4d4fddae6c5fe575daac3440806f8256..9c181affabb124646eb000073130a912f53ea880 100644 (file)
@@ -60,8 +60,6 @@ static void vbd_update(void){};
 #define BLKIF_STATE_DISCONNECTED 1
 #define BLKIF_STATE_CONNECTED    2
 
-#define WPRINTK(fmt, args...) printk(KERN_WARNING "xen_blk: " fmt, ##args)
-
 static int blkif_handle = 0;
 static unsigned int blkif_state = BLKIF_STATE_CLOSED;
 static unsigned int blkif_evtchn = 0;
@@ -694,7 +692,7 @@ int blkif_ioctl(struct inode *inode, struct file *filep,
         return -ENOSYS;
 
     default:
-        printk(KERN_ALERT "ioctl %08x not supported by XL blkif\n", command);
+        WPRINTK("ioctl %08x not supported by XL blkif\n", command);
         return -ENOSYS;
     }
     
@@ -1206,7 +1204,7 @@ static void blkif_connect(blkif_fe_interface_status_t *status)
     err = request_irq(blkif_irq, blkif_int, SA_SAMPLE_RANDOM, "blkif", NULL);
     if ( err )
     {
-        printk(KERN_ALERT "xen_blk: request_irq failed (err=%d)\n", err);
+        WPRINTK("request_irq failed (err=%d)\n", err);
         return;
     }
 
@@ -1353,7 +1351,7 @@ int wait_for_blkif(void)
 
     if ( blkif_state != BLKIF_STATE_CONNECTED )
     {
-        printk(KERN_INFO "xen_blk: Timeout connecting to device!\n");
+        WPRINTK("Timeout connecting to device!\n");
         err = -ENOSYS;
     }
     return err;
@@ -1367,7 +1365,7 @@ int __init xlblk_init(void)
          (xen_start_info.flags & SIF_BLK_BE_DOMAIN) )
         return 0;
 
-    printk(KERN_INFO "xen_blk: Initialising virtual block device driver\n");
+    IPRINTK("Initialising virtual block device driver\n");
 
     rec_ring_free = 0;
     for ( i = 0; i < BLKIF_RING_SIZE; i++ )
index b7fff75956527fad4cfe40f5976823e99cf85530..7f46506285e2221fa7a089f1d2c00e3d5dc3104a 100644 (file)
 #include <asm/atomic.h>
 #include <asm/uaccess.h>
 
+#define IPRINTK(fmt, args...) \
+    printk(KERN_INFO "xen_blk: " fmt, ##args)
+#define WPRINTK(fmt, args...) \
+    printk(KERN_WARNING "xen_blk: " fmt, ##args)
+
 #if 0
 #define DPRINTK(_f, _a...) printk ( KERN_ALERT _f , ## _a )
 #else
@@ -64,8 +69,7 @@
 
 struct xlbd_type_info {
     int partn_shift;
-    int partn_per_major;
-    int devs_per_major;
+    int disks_per_major;
     char *devname;
     char *diskname;
 };
index d7b6aa6aa1e43d51632d50ec0f37ca165610ed04..0110d11223421ab70a9b18ab65da5dd3a7e817e8 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "block.h"
 #include <linux/blkdev.h>
+#include <linux/list.h>
 
 /*
  * For convenience we distinguish between ide, scsi and 'other' (i.e.,
 #define NUM_SCSI_MAJORS 9
 #define NUM_VBD_MAJORS 1
 
+struct lvdisk
+{
+    blkif_sector_t capacity; /*  0: Size in terms of 512-byte sectors.   */
+    blkif_vdev_t   device;   /*  8: Device number (opaque 16 bit value). */
+    u16            info; 
+    struct list_head list;
+};
+
 static struct xlbd_type_info xlbd_ide_type = {
     .partn_shift = 6,
-    .partn_per_major = 2,
+    .disks_per_major = 2,
     .devname = "ide",
     .diskname = "hd",
 };
 
 static struct xlbd_type_info xlbd_scsi_type = {
     .partn_shift = 4,
-    .partn_per_major = 16,
+    .disks_per_major = 16,
     .devname = "sd",
     .diskname = "sd",
 };
 
 static struct xlbd_type_info xlbd_vbd_type = {
     .partn_shift = 4,
-    .partn_per_major = 16,
+    .disks_per_major = 16,
     .devname = "xvd",
     .diskname = "xvd",
 };
@@ -66,10 +75,17 @@ static struct xlbd_type_info xlbd_vbd_type = {
 static struct xlbd_major_info *major_info[NUM_IDE_MAJORS + NUM_SCSI_MAJORS +
                                          NUM_VBD_MAJORS];
 
+#define XLBD_MAJOR_IDE_START    0
+#define XLBD_MAJOR_SCSI_START   (NUM_IDE_MAJORS)
+#define XLBD_MAJOR_VBD_START    (NUM_IDE_MAJORS + NUM_SCSI_MAJORS)
+
+#define XLBD_MAJOR_IDE_RANGE    XLBD_MAJOR_IDE_START ... XLBD_MAJOR_SCSI_START - 1
+#define XLBD_MAJOR_SCSI_RANGE   XLBD_MAJOR_SCSI_START ... XLBD_MAJOR_VBD_START - 1
+#define XLBD_MAJOR_VBD_RANGE    XLBD_MAJOR_VBD_START ... XLBD_MAJOR_VBD_START + NUM_VBD_MAJORS - 1
+
 /* Information about our VBDs. */
 #define MAX_VBDS 64
-static int nr_vbds;
-static vdisk_t *vbd_info;
+struct list_head vbds_list;
 
 struct request_queue *xlbd_blk_queue = NULL;
 
@@ -82,388 +98,381 @@ static struct block_device_operations xlvbd_block_fops =
     .open  = blkif_open,
     .release = blkif_release,
     .ioctl  = blkif_ioctl,
-#if 0
-    check_media_change: blkif_check,
-    revalidate:         blkif_revalidate,
-#endif
 };
 
 spinlock_t blkif_io_lock = SPIN_LOCK_UNLOCKED;
 
-static int xlvbd_get_vbd_info(vdisk_t *disk_info)
+static struct lvdisk *xlvbd_device_alloc(void)
+{
+    struct lvdisk *disk;
+
+    disk = kmalloc(sizeof(*disk), GFP_KERNEL);
+    if (disk != NULL) {
+        memset(disk, 0, sizeof(*disk));
+        INIT_LIST_HEAD(&disk->list);
+    }
+    return disk;
+}
+
+static void xlvbd_device_free(struct lvdisk *disk)
+{
+    list_del(&disk->list);
+    kfree(disk);
+}
+
+static vdisk_t *xlvbd_probe(int *ret)
 {
-    vdisk_t         *buf = (vdisk_t *)__get_free_page(GFP_KERNEL);
-    blkif_request_t  req;
     blkif_response_t rsp;
-    int              nr;
+    blkif_request_t req;
+    vdisk_t *disk_info = NULL;
+    unsigned long buf;
+    int nr;
+
+    buf = __get_free_page(GFP_KERNEL);
+    if ((void *)buf == NULL)
+        goto out;
 
     memset(&req, 0, sizeof(req));
-    req.operation   = BLKIF_OP_PROBE;
+    req.operation = BLKIF_OP_PROBE;
     req.nr_segments = 1;
+#ifdef CONFIG_XEN_BLKDEV_GRANT
+    blkif_control_probe_send(&req, &rsp,
+                             (unsigned long)(virt_to_machine(buf)));
+#else
     req.frame_and_sects[0] = virt_to_machine(buf) | 7;
 
     blkif_control_send(&req, &rsp);
-
-    if ( rsp.status <= 0 )
-    {
-        printk(KERN_ALERT "Could not probe disks (%d)\n", rsp.status);
-        return -1;
+#endif
+    if ( rsp.status <= 0 ) {
+        WPRINTK("Could not probe disks (%d)\n", rsp.status);
+        goto out;
     }
-
-    if ( (nr = rsp.status) > MAX_VBDS )
+    nr = rsp.status;
+    if ( nr > MAX_VBDS )
         nr = MAX_VBDS;
-    memcpy(disk_info, buf, nr * sizeof(vdisk_t));
 
-    free_page((unsigned long)buf);
+    disk_info = kmalloc(nr * sizeof(vdisk_t), GFP_KERNEL);
+    if (disk_info != NULL)
+        memcpy(disk_info, (void *) buf, nr * sizeof(vdisk_t));
 
-    return nr;
+    if (ret != NULL)
+        *ret = nr;
+
+out:
+    free_page(buf);
+    return disk_info;
 }
 
-static struct xlbd_major_info *xlbd_get_major_info(int xd_device, int *minor)
+static struct xlbd_major_info *xlbd_alloc_major_info(
+    int major, int minor, int index)
 {
-    int mi_idx, new_major;
-    int xd_major = MAJOR_XEN(xd_device); 
-    int xd_minor = MINOR_XEN(xd_device);
-
-    *minor = xd_minor;
-
-    switch (xd_major) {
-    case IDE0_MAJOR: mi_idx = 0; new_major = IDE0_MAJOR; break;
-    case IDE1_MAJOR: mi_idx = 1; new_major = IDE1_MAJOR; break;
-    case IDE2_MAJOR: mi_idx = 2; new_major = IDE2_MAJOR; break;
-    case IDE3_MAJOR: mi_idx = 3; new_major = IDE3_MAJOR; break;
-    case IDE4_MAJOR: mi_idx = 4; new_major = IDE4_MAJOR; break;
-    case IDE5_MAJOR: mi_idx = 5; new_major = IDE5_MAJOR; break;
-    case IDE6_MAJOR: mi_idx = 6; new_major = IDE6_MAJOR; break;
-    case IDE7_MAJOR: mi_idx = 7; new_major = IDE7_MAJOR; break;
-    case IDE8_MAJOR: mi_idx = 8; new_major = IDE8_MAJOR; break;
-    case IDE9_MAJOR: mi_idx = 9; new_major = IDE9_MAJOR; break;
-    case SCSI_DISK0_MAJOR: mi_idx = 10; new_major = SCSI_DISK0_MAJOR; break;
-    case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR:
-        mi_idx = 11 + xd_major - SCSI_DISK1_MAJOR;
-        new_major = SCSI_DISK1_MAJOR + xd_major - SCSI_DISK1_MAJOR;
-        break;
-    case SCSI_CDROM_MAJOR: mi_idx = 18; new_major = SCSI_CDROM_MAJOR; break;
-    default: mi_idx = 19; new_major = 0;/* XXXcl notyet */ break;
-    }
-
-    if (major_info[mi_idx])
-        return major_info[mi_idx];
+    struct xlbd_major_info *ptr;
 
-    major_info[mi_idx] = kmalloc(sizeof(struct xlbd_major_info), GFP_KERNEL);
-    if (major_info[mi_idx] == NULL)
+    ptr = kmalloc(sizeof(struct xlbd_major_info), GFP_KERNEL);
+    if (ptr == NULL)
         return NULL;
 
-    memset(major_info[mi_idx], 0, sizeof(struct xlbd_major_info));
+    memset(ptr, 0, sizeof(struct xlbd_major_info));
 
-    switch (mi_idx) {
-    case 0 ... (NUM_IDE_MAJORS - 1):
-        major_info[mi_idx]->type = &xlbd_ide_type;
-        major_info[mi_idx]->index = mi_idx;
+    ptr->major = major;
+
+    switch (index) {
+    case XLBD_MAJOR_IDE_RANGE:
+        ptr->type = &xlbd_ide_type;
+        ptr->index = index - XLBD_MAJOR_IDE_START;
         break;
-    case NUM_IDE_MAJORS ... (NUM_IDE_MAJORS + NUM_SCSI_MAJORS - 1):
-        major_info[mi_idx]->type = &xlbd_scsi_type;
-        major_info[mi_idx]->index = mi_idx - NUM_IDE_MAJORS;
+    case XLBD_MAJOR_SCSI_RANGE:
+        ptr->type = &xlbd_scsi_type;
+        ptr->index = index - XLBD_MAJOR_SCSI_START;
         break;
-        case (NUM_IDE_MAJORS + NUM_SCSI_MAJORS) ...
-            (NUM_IDE_MAJORS + NUM_SCSI_MAJORS + NUM_VBD_MAJORS - 1):
-                major_info[mi_idx]->type = &xlbd_vbd_type;
-        major_info[mi_idx]->index = mi_idx -
-            (NUM_IDE_MAJORS + NUM_SCSI_MAJORS);
+    case XLBD_MAJOR_VBD_RANGE:
+        ptr->type = &xlbd_vbd_type;
+        ptr->index = index - XLBD_MAJOR_VBD_START;
         break;
     }
-    major_info[mi_idx]->major = new_major;
+    
+    if (register_blkdev(ptr->major, ptr->type->devname)) {
+        WPRINTK("can't get major %d with name %s\n",
+                ptr->major, ptr->type->devname);
+        kfree(ptr);
+        return NULL;
+    }
 
-    if (register_blkdev(major_info[mi_idx]->major, major_info[mi_idx]->type->devname)) {
-        printk(KERN_ALERT "XL VBD: can't get major %d with name %s\n",
-               major_info[mi_idx]->major, major_info[mi_idx]->type->devname);
-        goto out;
+    devfs_mk_dir(ptr->type->devname);
+    major_info[index] = ptr;
+    return ptr;
+}
+
+static struct xlbd_major_info *xlbd_get_major_info(int device)
+{
+    int major, minor, index;
+
+    major = MAJOR_XEN(device);
+    minor = MINOR_XEN(device);
+
+    switch (major) {
+    case IDE0_MAJOR: index = 0; break;
+    case IDE1_MAJOR: index = 1; break;
+    case IDE2_MAJOR: index = 2; break;
+    case IDE3_MAJOR: index = 3; break;
+    case IDE4_MAJOR: index = 4; break;
+    case IDE5_MAJOR: index = 5; break;
+    case IDE6_MAJOR: index = 6; break;
+    case IDE7_MAJOR: index = 7; break;
+    case IDE8_MAJOR: index = 8; break;
+    case IDE9_MAJOR: index = 9; break;
+    case SCSI_DISK0_MAJOR: index = 10; break;
+    case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR:
+        index = 11 + major - SCSI_DISK1_MAJOR;
+        break;
+    case SCSI_CDROM_MAJOR: index = 18; break;
+    default: index = 19; break;
     }
 
-    devfs_mk_dir(major_info[mi_idx]->type->devname);
+    return ((major_info[index] != NULL) ? major_info[index] :
+            xlbd_alloc_major_info(major, minor, index));
+}
 
-    return major_info[mi_idx];
+static int xlvbd_blk_queue_alloc(struct xlbd_type_info *type)
+{
+    xlbd_blk_queue = blk_init_queue(do_blkif_request, &blkif_io_lock);
+    if (xlbd_blk_queue == NULL)
+        return -1;
 
- out:
-    kfree(major_info[mi_idx]);
-    major_info[mi_idx] = NULL;
-    return NULL;
+    elevator_init(xlbd_blk_queue, "noop");
+
+    /*
+    * Turn off barking 'headactive' mode. We dequeue
+    * buffer heads as soon as we pass them to back-end
+    * driver.
+    */
+    blk_queue_headactive(xlbd_blk_queue, 0);
+
+    /* Hard sector size and max sectors impersonate the equiv. hardware. */
+    blk_queue_hardsect_size(xlbd_blk_queue, 512);
+    blk_queue_max_sectors(xlbd_blk_queue, 512);
+
+    /* Each segment in a request is up to an aligned page in size. */
+    blk_queue_segment_boundary(xlbd_blk_queue, PAGE_SIZE - 1);
+    blk_queue_max_segment_size(xlbd_blk_queue, PAGE_SIZE);
+
+    /* Ensure a merged request will fit in a single I/O ring slot. */
+    blk_queue_max_phys_segments(xlbd_blk_queue, BLKIF_MAX_SEGMENTS_PER_REQUEST);
+    blk_queue_max_hw_segments(xlbd_blk_queue, BLKIF_MAX_SEGMENTS_PER_REQUEST);
+
+    /* Make sure buffer addresses are sector-aligned. */
+    blk_queue_dma_alignment(xlbd_blk_queue, 511);
+    return 0;
 }
 
-static struct gendisk *xlvbd_get_gendisk(struct xlbd_major_info *mi,
-                                         int xd_minor, vdisk_t *xd)
+struct gendisk *xlvbd_alloc_gendisk(
+    struct xlbd_major_info *mi, int minor, vdisk_t *disk)
 {
     struct gendisk *gd;
     struct xlbd_disk_info *di;
-    int device, partno;
-
-    device = MKDEV(mi->major, xd_minor);
-    gd = get_gendisk(device, &partno);
-    if ( gd != NULL )
-        return gd;
+    int nr_minors = 1;
 
     di = kmalloc(sizeof(struct xlbd_disk_info), GFP_KERNEL);
-    if ( di == NULL )
-        return NULL;
+    if (di == NULL)
+        goto out;
     di->mi = mi;
-    di->xd_device = xd->device;
+    di->xd_device = disk->device;
 
-    /* Construct an appropriate gendisk structure. */
-    gd = alloc_disk(1);
-    if ( gd == NULL )
+    /* Full disk rather than a single partition? */
+    if ((minor & ((1 << mi->type->partn_shift) - 1)) == 0)
+        nr_minors = 1 << mi->type->partn_shift;
+
+    gd = alloc_disk(nr_minors);
+    if ( !gd )
         goto out;
 
+    if ( nr_minors > 1 ) /* full disk? */
+        sprintf(gd->disk_name, "%s%c", mi->type->diskname,
+                'a' + mi->index * mi->type->disks_per_major +
+                    (minor >> mi->type->partn_shift));
+    else
+        sprintf(gd->disk_name, "%s%c%d", mi->type->diskname,
+                'a' + mi->index * mi->type->disks_per_major +
+                (minor >> mi->type->partn_shift),
+                minor & ((1 << mi->type->partn_shift) - 1));
+
     gd->major = mi->major;
-    gd->first_minor = xd_minor;
+    gd->first_minor = minor;
     gd->fops = &xlvbd_block_fops;
     gd->private_data = di;
-    sprintf(gd->disk_name, "%s%c%d", mi->type->diskname,
-            'a' + mi->index * mi->type->partn_per_major +
-            (xd_minor >> mi->type->partn_shift),
-            xd_minor & ((1 << mi->type->partn_shift) - 1));
-
-    set_capacity(gd, xd->capacity);
-
-    if ( xlbd_blk_queue == NULL )
-    {
-        xlbd_blk_queue = blk_init_queue(do_blkif_request,
-                                        &blkif_io_lock);
-        if ( xlbd_blk_queue == NULL )
-            goto out;
-        elevator_init(xlbd_blk_queue, "noop");
-
-        /*
-         * Turn off barking 'headactive' mode. We dequeue buffer heads as soon 
-         * as we pass them to back-end driver.
-         */
-        blk_queue_headactive(xlbd_blk_queue, 0);
-
-        blk_queue_hardsect_size(xlbd_blk_queue, 512);
-        blk_queue_max_sectors(xlbd_blk_queue, 512);
-
-        /* Each segment in a request is up to an aligned page in size. */
-        blk_queue_segment_boundary(xlbd_blk_queue, PAGE_SIZE - 1);
-        blk_queue_max_segment_size(xlbd_blk_queue, PAGE_SIZE);
-
-        /* Ensure a merged request will fit in a single I/O ring slot. */
-        blk_queue_max_phys_segments(
-            xlbd_blk_queue, BLKIF_MAX_SEGMENTS_PER_REQUEST);
-        blk_queue_max_hw_segments(
-            xlbd_blk_queue, BLKIF_MAX_SEGMENTS_PER_REQUEST);
-
-        /* Make sure buffer addresses are sector-aligned. */
-        blk_queue_dma_alignment(xlbd_blk_queue, 511);
-    }
-    gd->queue = xlbd_blk_queue;
+    set_capacity(gd, disk->capacity);
 
-    add_disk(gd);
+    if ((xlbd_blk_queue == NULL) && xlvbd_blk_queue_alloc(mi->type))
+            goto out_gendisk;
 
+    gd->queue = xlbd_blk_queue;
+    add_disk(gd);
     return gd;
 
- out:
-    if ( gd != NULL )
-        del_gendisk(gd);
+out_gendisk:
+    del_gendisk(gd);
+out:
     kfree(di);
     return NULL;
 }
 
-/*
- * xlvbd_init_device - initialise a VBD device
- * @disk:              a vdisk_t describing the VBD
- *
- * Takes a vdisk_t * that describes a VBD the domain has access to.
- * Performs appropriate initialisation and registration of the device.
- *
- * Care needs to be taken when making re-entrant calls to ensure that
- * corruption does not occur.  Also, devices that are in use should not have
- * their details updated.  This is the caller's responsibility.
- */
-static int xlvbd_init_device(vdisk_t *xd)
+static int xlvbd_device_add(struct list_head *list, vdisk_t *disk)
 {
+    struct lvdisk *new;
+    int minor;
+    dev_t device;
     struct block_device *bd;
     struct gendisk *gd;
     struct xlbd_major_info *mi;
-    int device;
-    int minor;
 
-    int err = -ENOMEM;
-
-    mi = xlbd_get_major_info(xd->device, &minor);
+    mi = xlbd_get_major_info(disk->device);
     if (mi == NULL)
         return -EPERM;
 
+    new = xlvbd_device_alloc();
+    if (new == NULL)
+        return -1;
+    new->capacity = disk->capacity;
+    new->device = disk->device;
+    new->info = disk->info;
+    
+    minor = MINOR_XEN(disk->device);
     device = MKDEV(mi->major, minor);
-
-    if ((bd = bdget(device)) == NULL)
-        return -EPERM;
-
-    /*
-     * Update of partition info, and check of usage count, is protected
-     * by the per-block-device semaphore.
-     */
-    down(&bd->bd_sem);
-
-    gd = xlvbd_get_gendisk(mi, minor, xd);
-    if (gd == NULL) {
-        err = -EPERM;
+    
+    bd = bdget(device);
+    if (bd == NULL)
         goto out;
-    }
+    
+    gd = xlvbd_alloc_gendisk(mi, minor, disk);
+    if (gd == NULL)
+        goto out_bd;
 
-    if (VDISK_READONLY(xd->info))
-        set_disk_ro(gd, 1); 
+    if (VDISK_READONLY(disk->info))
+        set_disk_ro(gd, 1);
 
-    /* Some final fix-ups depending on the device type */
-    switch (VDISK_TYPE(xd->info)) { 
+    switch (VDISK_TYPE(disk->info)) {
     case VDISK_TYPE_CDROM:
-        gd->flags |= GENHD_FL_REMOVABLE | GENHD_FL_CD; 
-        /* FALLTHROUGH */
+        gd->flags |= GENHD_FL_REMOVABLE | GENHD_FL_CD;
+        break;
     case VDISK_TYPE_FLOPPY: 
     case VDISK_TYPE_TAPE:
-        gd->flags |= GENHD_FL_REMOVABLE; 
-        break; 
-
+        gd->flags |= GENHD_FL_REMOVABLE;
+        break;
     case VDISK_TYPE_DISK:
-        break; 
-
+        break;
     default:
-        printk(KERN_ALERT "XenLinux: unknown device type %d\n", 
-               VDISK_TYPE(xd->info)); 
-        break; 
-    }
+        WPRINTK("unknown device type %d\n", VDISK_TYPE(disk->info));
+        break;
+    }    
 
-    err = 0;
- out:
-    up(&bd->bd_sem);
-    bdput(bd);    
-    return err;
+    list_add(&new->list, list);
+out_bd:
+    bdput(bd);
+out:
+    return 0;
 }
 
-/*
- * xlvbd_remove_device - remove a device node if possible
- * @device:       numeric device ID
- *
- * Updates the gendisk structure and invalidates devices.
- *
- * This is OK for now but in future, should perhaps consider where this should
- * deallocate gendisks / unregister devices.
- */
-static int xlvbd_remove_device(int dev16)
+static int xlvbd_device_del(struct lvdisk *disk)
 {
-    int i, rc = 0;
-    struct gendisk *gd;
+    dev_t device;
     struct block_device *bd;
+    struct gendisk *gd;
     struct xlbd_disk_info *di;
-    dev_t device = MKDEV(MAJOR_XEN(dev16), MINOR_XEN(dev16));
+    int ret = 0, unused;
+
+    device = MKDEV(MAJOR_XEN(disk->device), MINOR_XEN(disk->device));
 
     bd = bdget(device);
-    if (!bd)
+    if (bd == NULL)
         return -1;
 
-    /*
-     * Update of partition info, and check of usage count, is protected
-     * by the per-block-device semaphore.
-     */
-    down(&bd->bd_sem);
-
-    gd = get_gendisk(device, &i);
-    BUG_ON(gd == NULL);
-    di = (struct xlbd_disk_info *) gd->private_data;
-    BUG_ON(di == NULL);
-
-    if ( di->mi->usage != 0 )
-    {
-        printk(KERN_ALERT "VBD removal failed - in use [dev=%x]\n", device);
-        rc = -1;
+    gd = get_gendisk(device, &unused);
+    di = gd->private_data;
+
+    if (di->mi->usage != 0) {
+        WPRINTK("disk removal failed: used [dev=%x]\n", device);
+        ret = -1;
         goto out;
     }
 
-    /* The VBD is mapped to an entire unit. */
-    
-    invalidate_partition(gd, 0);
-    set_capacity(gd, 0);
+    del_gendisk(gd);
 
+    xlvbd_device_free(disk);
 out:
-    up(&bd->bd_sem);
     bdput(bd);
-    return rc;
+    return ret;
 }
 
-/*
- * xlvbd_update_vbds - reprobes the VBD status and performs updates driver
- * state. The VBDs need to be updated in this way when the domain is
- * initialised and also each time we receive an XLBLK_UPDATE event.
- */
-void xlvbd_update_vbds(void)
+static int xlvbd_device_update(struct lvdisk *ldisk, vdisk_t *disk)
 {
-    int i, j, k, old_nr, new_nr;
-    vdisk_t *old_info, *new_info, *merged_info;
+    dev_t device;
+    struct block_device *bd;
+    struct gendisk *gd;
+    int unused;
 
-    old_info = vbd_info;
-    old_nr   = nr_vbds;
+    if ((ldisk->capacity == disk->capacity) && (ldisk->info == disk->info))
+        return 0;    
 
-    new_info = kmalloc(MAX_VBDS * sizeof(vdisk_t), GFP_KERNEL);
-    if (!new_info)
-        return;
+    device = MKDEV(MAJOR_XEN(ldisk->device), MINOR_XEN(ldisk->device));
 
-    if ( unlikely(new_nr = xlvbd_get_vbd_info(new_info)) < 0 )
-        goto out;
+    bd = bdget(device);
+    if (bd == NULL)
+        return -1;
 
-    /*
-     * Final list maximum size is old list + new list. This occurs only when
-     * old list and new list do not overlap at all, and we cannot yet destroy
-     * VBDs in the old list because the usage counts are busy.
-     */
-    merged_info = kmalloc((old_nr + new_nr) * sizeof(vdisk_t), GFP_KERNEL);
-    if (!merged_info)
-        goto out;
+    gd = get_gendisk(device, &unused);
+    set_capacity(gd, disk->capacity);    
+    ldisk->capacity = disk->capacity;
 
-    /* @i tracks old list; @j tracks new list; @k tracks merged list. */
-    i = j = k = 0;
+    bdput(bd);
 
-    while ( (i < old_nr) && (j < new_nr) )
-    {
-        if ( old_info[i].device < new_info[j].device )
-        {
-            if ( xlvbd_remove_device(old_info[i].device) != 0 )
-                memcpy(&merged_info[k++], &old_info[i], sizeof(vdisk_t));
-            i++;
-        }
-        else if ( old_info[i].device > new_info[j].device )
-        {
-            if ( xlvbd_init_device(&new_info[j]) == 0 )
-                memcpy(&merged_info[k++], &new_info[j], sizeof(vdisk_t));
-            j++;
-        }
-        else
-        {
-            if ( ((old_info[i].capacity == new_info[j].capacity) &&
-                  (old_info[i].info == new_info[j].info)) ||
-                 (xlvbd_remove_device(old_info[i].device) != 0) )
-                memcpy(&merged_info[k++], &old_info[i], sizeof(vdisk_t));
-            else if ( xlvbd_init_device(&new_info[j]) == 0 )
-                memcpy(&merged_info[k++], &new_info[j], sizeof(vdisk_t));
-            i++; j++;
-        }
-    }
+    return 0;
+}
 
-    for ( ; i < old_nr; i++ )
-    {
-        if ( xlvbd_remove_device(old_info[i].device) != 0 )
-            memcpy(&merged_info[k++], &old_info[i], sizeof(vdisk_t));
+void xlvbd_refresh(void)
+{
+    vdisk_t *newdisks;
+    struct list_head *tmp, *tmp2;
+    struct lvdisk *disk;
+    int i, nr;
+
+    newdisks = xlvbd_probe(&nr);
+    if (newdisks == NULL) {
+        WPRINTK("failed to probe\n");
+        return;
     }
-
-    for ( ; j < new_nr; j++ )
-    {
-        if ( xlvbd_init_device(&new_info[j]) == 0 )
-            memcpy(&merged_info[k++], &new_info[j], sizeof(vdisk_t));
+    
+    i = 0;
+    list_for_each_safe(tmp, tmp2, &vbds_list) {
+        disk = list_entry(tmp, struct lvdisk, list);
+        
+        for (i = 0; i < nr; i++) {
+            if ( !newdisks[i].device )
+                continue;
+            if ( disk->device == newdisks[i].device ) {
+                xlvbd_device_update(disk, &newdisks[i]);
+                newdisks[i].device = 0;
+                break;
+            }
+        }
+        if (i == nr) {
+            xlvbd_device_del(disk);
+            newdisks[i].device = 0;
+        }
     }
+    for (i = 0; i < nr; i++)
+        if ( newdisks[i].device )
+            xlvbd_device_add(&vbds_list, &newdisks[i]);
+    kfree(newdisks);
+}
 
-    vbd_info = merged_info;
-    nr_vbds  = k;
-
-    kfree(old_info);
-out:
-    kfree(new_info);
+/*
+ * xlvbd_update_vbds - reprobes the VBD status and performs updates driver
+ * state. The VBDs need to be updated in this way when the domain is
+ * initialised and also each time we receive an XLBLK_UPDATE event.
+ */
+void xlvbd_update_vbds(void)
+{
+    xlvbd_refresh();
 }
 
 /*
@@ -477,26 +486,22 @@ out:
  */
 int xlvbd_init(void)
 {
-    int i;
+    int i, nr;
+    vdisk_t *disks;
 
-    memset(major_info, 0, sizeof(major_info));
+    INIT_LIST_HEAD(&vbds_list);
 
-    vbd_info = kmalloc(MAX_VBDS * sizeof(vdisk_t), GFP_KERNEL);
-    if (vbd_info == NULL) {
-        printk(KERN_ALERT "Failed to allocate memory for disk info.\n");
-        nr_vbds = 0;
-        return 0;
+    memset(major_info, 0, sizeof(major_info));
+    
+    disks = xlvbd_probe(&nr);
+    if (disks == NULL) {
+        WPRINTK("failed to probe\n");
+        return -1;
     }
 
-    nr_vbds  = xlvbd_get_vbd_info(vbd_info);
-    if (nr_vbds < 0) {
-        kfree(vbd_info);
-        vbd_info = NULL;
-        nr_vbds  = 0;
-    } else {
-        for (i = 0; i < nr_vbds; i++)
-            xlvbd_init_device(&vbd_info[i]);
-    }
+    for (i = 0; i < nr; i++)
+        xlvbd_device_add(&vbds_list, &disks[i]);
 
+    kfree(disks);
     return 0;
 }